/**
 * @作者 伟子涵
 * @微信公号 搬砖暗夜码农
 * @开源项目 $ https://gitee.com/zhouwei5208888
 * @我的博客 $ http://itzhouwei.com
 * @创建时间 2023/11/30 19:10
 */
package com.itzhouwei.mybatis.binding;

import cn.hutool.core.lang.ClassScanner;
import com.itzhouwei.mybatis.session.SqlSession;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * @description: $MapperRegistry 映射器注册机
 * MapperRegistry 映射器注册类的核心主要在于提供了 ClassScanner.scanPackage 扫描包路径，
 * 调用 addMapper 方法，给接口类创建 MapperProxyFactory 映射器代理类，并写入到 knownMappers 的 HashMap 缓存中。
 * 另外就是这个类也提供了对应的 getMapper 获取映射器代理类的方法，
 * 其实这步就包装了我们上一章节手动操作实例化的过程，更加方便在 DefaultSqlSession 中
 * 获取 Mapper 时进行使用。
 * @author：zw，微信：yingshengzw
 * @date: 2023/11/30 19:10
 * @Copyright： 公众号：搬砖暗夜码农 | 博客：https://itzhouwei.com - 沉淀、分享、成长，让自己和他人都能有所收获！
 */
@SuppressWarnings("all")
public class MapperRegistry {

    /**
     * <h1>将已添加的映射器代理加入到 HashMap<h1/>
     */
    private  final Map<Class<?>, MapperProxyFactory<?>> knownMappers =new HashMap<>();


    /***
     * 获取映射器
     * @param type
     * @param sqlSession
     * @return
     * @param <T>
     */
    public <T> T getMapper(Class<?> type, SqlSession sqlSession) {
        final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) this.knownMappers.get(type);
        if (mapperProxyFactory == null) {
            throw new RuntimeException("没有找到对应的映射器");
        }
        try {
            return mapperProxyFactory.newInstance(sqlSession);
        }catch (Exception e) {
            throw new RuntimeException("创建映射器代理失败");
        }
    }


    /**
     * 添加映射器
     * @param type
     * @param <T>
     */
    public <T> void addMapper(Class<T> type) {
       //mapper 必须是接口 才会进行注册
        if (type.isInterface()) {
            if(hasKey(type)){
                // 如果重复添加了，报错
                throw new RuntimeException("Type " + type + " is already known to the MapperRegistry.");
            }
            //注册映射器代理工厂
            this.knownMappers.put(type, new MapperProxyFactory<T>(type));
        }
    }

    /***
     * 判断 Class<T> type 是否已经存在
     * @param type
     * @return
     * @param <T>
     */
    private <T> boolean hasKey(Class<T> type) {
         return this.knownMappers.containsKey(type);
    }

    /**
     * 扫描包路径，添加映射器
     * @param packageName
     */
    public  void addMapper(String packageName){
        Set<Class<?>> classes = ClassScanner.scanPackage(packageName);
        for (Class<?> aClass : classes) {
            addMapper(aClass);
        }
    }
}
